#ifndef __VECTOR_H__
#define __VECTOR_H__

#include <cmath>


class Vector4
{
public:
	Vector4(void) : m_fX(0.0f), m_fY(0.0f), m_fZ(0.0f), m_fW(0.0f)
	{}

	Vector4(const float x, const float y, const float z) : m_fX(x), m_fY(y), m_fZ(z), m_fW(1.0f)
	{}

	Vector4(const float x, const float y, const float z, const float w) : m_fX(x), m_fY(y), m_fZ(z), m_fW(w)
	{}

	Vector4(const Vector4 & rhs) : m_fX(rhs.m_fX), m_fY(rhs.m_fY), m_fZ(rhs.m_fZ), m_fW(rhs.m_fW)
	{}

	~Vector4(void)
	{}

	inline Vector4 & operator+=(const Vector4 & rhs)
	{
		m_fX += rhs.m_fX;
		m_fY += rhs.m_fY;
		m_fZ += rhs.m_fZ;
		m_fW = 1.0f;
		return *this;
	}

	inline Vector4 & operator-=(const Vector4 & rhs)
	{
		m_fX += rhs.m_fX;
		m_fY += rhs.m_fY;
		m_fZ += rhs.m_fZ;
		m_fW = 1.0f;
		return *this;
	}

	inline float dot3(const Vector4 & rhs) const
	{
		return (m_fX * rhs.m_fX + m_fY * rhs.m_fY + m_fZ * rhs.m_fZ);
	}

	inline float dot4(const Vector4 & rhs) const
	{
		return (m_fX * rhs.m_fX + m_fY * rhs.m_fY + m_fZ * rhs.m_fZ + m_fW * rhs.m_fW);
	}

	inline Vector4 cross(const Vector4 & rhs) const
	{
		return Vector4(m_fY * rhs.m_fZ - m_fZ * rhs.m_fY, m_fZ * rhs.m_fX - m_fX * rhs.m_fZ, m_fX * rhs.m_fY - m_fY * rhs.m_fX, 1.0f);
	}

	inline Vector4 & operator*=(const float s)
	{
		m_fX *= s;
		m_fY *= s;
		m_fZ *= s;
		m_fW  = 1.0f;
		return *this;
	}

	inline Vector4 & operator/=(const float s)
	{
		m_fX /= s;
		m_fY /= s;
		m_fZ /= s;
		m_fW = 1.0f;
		return *this;
	}

	inline bool operator==(const Vector4 & rhs) const
	{
		return ((m_fX == rhs.m_fX) && (m_fY == rhs.m_fY) && (m_fZ == rhs.m_fZ) && (m_fW == rhs.m_fW));
	}


	inline float length(void) const
	{
		return sqrt(m_fX * m_fX + m_fY * m_fY + m_fZ * m_fZ);
	}

	inline float lengthSqr(void) const
	{
		return (m_fX * m_fX + m_fY * m_fY + m_fZ * m_fZ);
	}

	Vector4 normalise(void)
	{
		float tmp_Length = 0.0f;
		Vector4 zeroVector = Vector4(0.0f, 0.0f, 0.0f, 0.0f);

		tmp_Length = this->length();
		if(tmp_Length < 0.00001f)
			return zeroVector;
		return Vector4((*this).m_fX / tmp_Length, (*this).m_fY / tmp_Length, (*this).m_fZ / tmp_Length, 1.0f);
	}

	void normaliseSelf(void)
	{
		float tmp_Length = 0.0f;
		Vector4 zeroVector = Vector4(0.0f, 0.0f, 0.0f, 0.0f);
		
		tmp_Length = this->length();
		if(tmp_Length < 0.0001f)
			*this = zeroVector;
		else
			*this /= this->length();
	}

	inline bool isZeroVector(void) const
	{
		if(m_fX != 0.0)return false;
		if(m_fY != 0.0)return false;
		if(m_fZ != 0.0)return false;
		if(m_fW != 0.0)return false;
		return true;
	}

	Vector4 lerp(const Vector4 & rhs, const float s) const
	{
		return Vector4(m_fX + s * (rhs.m_fX - m_fX), m_fY + s * (rhs.m_fY - m_fY), m_fZ + s * (rhs.m_fZ - m_fZ), 1.0f);
	}

	void truncate(const float fMaxLngth)
	{
		if (this->length() > fMaxLngth)
		{
			normaliseSelf();
			*this *= fMaxLngth;
		}
	}

	inline void zero()
	{
		m_fX=0.0f;
		m_fY=0.0f;
		m_fZ=0.0f;
		m_fW=0.0f;
	}

	float m_fX;
	float m_fY;
	float m_fZ;
	float m_fW;
};



inline Vector4 operator + (const Vector4 &v1,
						   const Vector4 &v2)
{
	return Vector4(v1.m_fX + v2.m_fX, v1.m_fY + v2.m_fY, v1.m_fZ + v2.m_fZ, 1.0f);
}

inline Vector4 operator - (const Vector4 &v1,
						   const Vector4 &v2)
{
	return Vector4(v1.m_fX - v2.m_fX, v1.m_fY - v2.m_fY, v1.m_fZ - v2.m_fZ, 1.0f);
}

inline Vector4 operator - (const Vector4 &v1)
{
	return Vector4(-v1.m_fX, -v1.m_fY, -v1.m_fZ, 1.0f);
}

inline Vector4 operator * (const Vector4 &v,
						   const float &s)
{
	return Vector4(v.m_fX * s, v.m_fY * s, v.m_fZ * s, 1.0f);
}

inline Vector4 operator * (const float & s,
						   const Vector4 &v)
{
	return Vector4(v.m_fX * s, v.m_fY * s, v.m_fZ * s, 1.0f);
}

inline Vector4 operator / (const Vector4 &v,
						   const float & s)
{
	return Vector4(v.m_fX / s, v.m_fY / s, v.m_fZ / s, 1.0f);
}

inline Vector4 crossProduct (const Vector4 &v1,
							 const Vector4 &v2)
{
	return Vector4(v1.m_fY * v2.m_fZ - v1.m_fZ * v2.m_fY,
				   v1.m_fZ * v2.m_fX - v1.m_fX * v2.m_fZ,
				   v1.m_fX * v2.m_fY - v1.m_fY * v2.m_fX,
				   1.0f);
}

inline float dotProduct4(const Vector4 &v1,
						 const Vector4 &v2)
{
	return (v1.m_fX * v2.m_fX + v1.m_fY * v2.m_fY + v1.m_fZ * v2.m_fZ + v1.m_fW * v2.m_fW);
}

inline float dotProduct3(const Vector4 &v1,
						 const Vector4 &v2)
{
	return (v1.m_fX * v2.m_fX + v1.m_fY * v2.m_fY + v1.m_fZ * v2.m_fZ);
}



#endif // __VECTOR_H__